﻿using System;
using System.Collections.Generic;
using System.Text;

namespace AJAXChatClient
{
    public class AjaxChatConfig
    {
        public class DOM_IDs
        {
            private string chatList = "chatList";
            private string onlineList = "onlineList";
            private string inputField = "inputField";
            private string messageLengthCounter = "messageLengthCounter";
            private string channelSelection = "channelSelection";
            private string styleSelection = "styleSelection";
            private string emoticonsContainer = "emoticonsContainer";
            private string colorCodesContainer = "colorCodesContainer";
            private string flashInterfaceContainer = "flashInterfaceContainer";
            private string pmAlertContainer = "PMAlertContainer";

            public DOM_IDs()
            {
            }

            /// <summary>
            /// The ID of the chat messages list.
            /// </summary>
            public string ChatList
            {
                get { return this.chatList; }
                set
                {
                    if (this.chatList != value)
                    {
                        this.chatList = value;
                    }
                }
            }

            /// <summary>
            /// The ID of the online users list.
            /// </summary>
            public string OnlineList
            {
                get { return this.onlineList; }
                set
                {
                    if (this.onlineList != value)
                    {
                        this.onlineList = value;
                    }
                }
            }

            /// <summary>
            /// The ID of the message text input field.
            /// </summary>
            public string InputField
            {
                get { return this.inputField; }
                set
                {
                    if (this.inputField != value)
                    {
                        this.inputField = value;
                    }
                }
            }

            /// <summary>
            /// The ID of the message text length counter.
            /// </summary>
            public string MessageLengthCounter
            {
                get { return this.messageLengthCounter; }
                set
                {
                    if (this.messageLengthCounter != value)
                    {
                        this.messageLengthCounter = value;
                    }
                }
            }

            /// <summary>
            /// The ID of the channel selection.
            /// </summary>
            public string ChannelSelection
            {
                get { return this.channelSelection; }
                set
                {
                    if (this.channelSelection != value)
                    {
                        this.channelSelection = value;
                    }
                }
            }

            /// <summary>
            /// The ID of the style selection.
            /// </summary>
            public string StyleSelection
            {
                get { return this.styleSelection; }
                set
                {
                    if (this.styleSelection != value)
                    {
                        this.styleSelection = value;
                    }
                }
            }

            /// <summary>
            /// The ID of the emoticons container.
            /// </summary>
            public string EmoticonsContainer
            {
                get { return this.emoticonsContainer; }
                set
                {
                    if (this.emoticonsContainer != value)
                    {
                        this.emoticonsContainer = value;
                    }
                }
            }

            /// <summary>
            /// The ID of the color codes container.
            /// </summary>
            public string ColorCodesContainer
            {
                get { return this.colorCodesContainer; }
                set
                {
                    if (this.colorCodesContainer != value)
                    {
                        this.colorCodesContainer = value;
                    }
                }
            }

            /// <summary>
            /// The ID of the flash interface container.
            /// </summary>
            public string FlashInterfaceContainer
            {
                get { return this.flashInterfaceContainer; }
                set
                {
                    if (this.flashInterfaceContainer != value)
                    {
                        this.flashInterfaceContainer = value;
                    }
                }
            }

            /// <summary>
            /// The ID of the SMF Private Messages Alert container
            /// </summary>
            /// <remarks>
            /// AJAX Chat to SMF Private Messages Integration
            /// 5 February 2013 - Peter L Jones
            /// </remarks>
            public string PMAlertContainer
            {
                get { return this.pmAlertContainer; }
                set
                {
                    if (this.pmAlertContainer != value)
                    {
                        this.pmAlertContainer = value;
                    }
                }
            }

            private static string[] sKeys = null;

            public static string[] GetKeys()
            {
                if (sKeys == null)
                {
                    try
                    {
                        System.Reflection.PropertyInfo[] props = typeof(DOM_IDs).GetProperties(
                            System.Reflection.BindingFlags.Public |
                            System.Reflection.BindingFlags.GetProperty |
                            System.Reflection.BindingFlags.SetProperty);
                        string[] keys = new string[props.Length];
                        for (int i = props.Length - 1; i >= 0; i--)
                        {
                            keys[i] = props[i].Name;
                        }
                        sKeys = keys;
                    }
                    catch (Exception)
                    {
                    }
                }
                return sKeys;
            }

            public string GetID(string key)
            {
                try
                {
                    System.Reflection.PropertyInfo prop = typeof(DOM_IDs).GetProperty(key,
                        System.Reflection.BindingFlags.IgnoreCase |
                        System.Reflection.BindingFlags.Public |
                        System.Reflection.BindingFlags.GetProperty |
                        System.Reflection.BindingFlags.SetProperty);
                    return prop.GetValue(this,
                        System.Reflection.BindingFlags.GetProperty, null, null,
                        System.Globalization.CultureInfo.CurrentCulture).ToString();
                }
                catch (Exception)
                {
                    return null;
                }
            }
        }

        public struct Sound_Files
        {
            public string Sound_1 = "sound_1.mp3";
            public string Sound_2 = "sound_2.mp3";
            public string Sound_3 = "sound_3.mp3";
            public string Sound_4 = "sound_4.mp3";
            public string Sound_5 = "sound_5.mp3";
            public string Sound_6 = "sound_6.mp3";

            public Sound_Files()
            {
            }

            public bool Equals(Sound_Files other)
            {
                return this.Sound_1 == other.Sound_1
                    && this.Sound_2 == other.Sound_2
                    && this.Sound_3 == other.Sound_3
                    && this.Sound_4 == other.Sound_4
                    && this.Sound_5 == other.Sound_5
                    && this.Sound_6 == other.Sound_6;
            }

            public override bool Equals(object obj)
            {
                return obj is Sound_Files && this.Equals((Sound_Files)obj);
            }

            public override int GetHashCode()
            {
                return this.Sound_1.GetHashCode()
                     ^ this.Sound_2.GetHashCode()
                     ^ this.Sound_3.GetHashCode()
                     ^ this.Sound_4.GetHashCode()
                     ^ this.Sound_5.GetHashCode()
                     ^ this.Sound_6.GetHashCode();
            }

            public static readonly string[] Keys = new string[]
            {
                "Sound_1", "Sound_2", "Sound_3",
                "Sound_4", "Sound_5", "Sound_6"
            };

            public string this[string key]
            {
                get
                {
                    if (string.IsNullOrEmpty(key))
                        return null;
                    key = key.ToLower();
                    switch (key)
                    {
                        case "sound_1":
                            return this.Sound_1;
                        case "sound_2":
                            return this.Sound_2;
                        case "sound_3":
                            return this.Sound_3;
                        case "sound_4":
                            return this.Sound_4;
                        case "sound_5":
                            return this.Sound_5;
                        case "sound_6":
                            return this.Sound_6;
                        default:
                            return null;
                    }
                }
            }
        }

        private int? loginChannelID = null;
        private string loginChannelName = null;
        private int timerRate = 2000;
        private string ajaxURL = "./?ajax=true";
        private string baseURL = "./";
        private string regExpMediaURL = "^((http)|(https))://";
        private bool startChatOnLoad = true;
        private DOM_IDs domIDs;
        private AjaxChatSettings settings;
        
        private string[] nonPersistentSettings;
        
        private string[] bbCodeTags = new string[] {
            "b",
            "i",
            "u",
            "quote",
            "code",
            "color",
            "url",
            "img"
        };
        
        private string[] colorCodes = new string[] {
            "gray",
            "silver",
            "white",
            "yellow",
            "orange",
            "red",
            "fushia",
            "purple",
            "navy",
            "blue",
            "aqua",
            "teal",
            "green",
            "lime",
            "olive",
            "maroon",
            "black"
        };

        private string[] emoticonCodes = new string[] {
            ":green",
            ":amber",
            ":red",
            ":)",
            ":D",
            ":lol",
            ":titter",
            ":yay",
            ":oww",
            ";)",
            ":P",
            ":*",
            ":good",
            ":evil",
            ":(",
            ";(",
            ":?",
            ":shrug",
            ":mmm",
            ":hmm",
            ":hmph",
            ":tsk",
            ":O",
            ":<",
            ":ick",
            ":oops",
            ":doh",
            ":headdesk",
            ":duh",
            ":|",
            ":^",
            ":v",
            ":yawn",
            ":kill",
            ":hug",
            ":grouphug",
            ":popcorn",
            ":chocolate",
            ":toilet",
            ":shower",
            ":food",
            ":coffee",
            ":tea",
            ":pop",
            ":wine",
            ":beer",
            ":cocktail",
            ":bed",
            ":sex",
            ":fag",
            ":study",
            ":work",
            ":out",
            ":travel",
            ":phone",
            ":chores",
            ":diy",
            ":drugs",
            ":rocknroll",
            ":birthday",
            ":dead",
            ":monkey",
            ":creeper",
            ":sniff",
            ":extra1",
            ":extra2",
            ":extra3",
            ":extra4"
        };

        private string[] emoticonFiles = new string[] {
            "plumbob.gif",
            "plumbob_amber.gif",
            "plumbob_red.gif",
            "smile.png",
            "grin.png",
            "lol.png",
            "titter.png",
            "yay.png",
            "oww.png",
            "wink.png",
            "tongue.png",
            "kiss.png",
            "good.png",
            "evil.png",
            "sad.png",
            "cry.png",
            "huh.png",
            "shrug.png",
            "mmm.png",
            "hmm.png",
            "hmph.png",
            "tsk.png",
            "gasp.png",
            "grr.png",
            "ick.png",
            "oops.png",
            "doh.png",
            "headdesk.png",
            "duh.png",
            "stern.png",
            "thumbsup.png",
            "thumbsdown.png",
            "yawn.png",
            "kill.png",
            "hug.png",
            "grouphug.png",
            "popcorn.png",
            "chocolate.png",
            "toilet.png",
            "shower.png",
            "food.png",
            "coffee.png",
            "tea.png",
            "pop.png",
            "wine.png",
            "beer.png",
            "cocktail.png",
            "bed.png",
            "sex.png",
            "fag.png",
            "study.png",
            "work.png",
            "out.png",
            "travel.png",
            "phone.png",
            "chores.png",
            "diy.png",
            "drugs.png",
            "rock.png",
            "birthday.png",
            "dead.png",
            "monkey.png",
            "creeper.png",
            "sniff.png",
            "extra1.png",
            "extra2.png",
            "extra3.png",
            "extra4.png"
        };

        private Sound_Files soundFiles;

        private string[] userIcons = new string[] {
            "1","3","4","5","6","8","18","23","36","109","191","232","267","302","303","304",
            "442","511","595","637","651","662","706","708","803","839","867","962",
            "969","978","1088","1214","1435","1476","1702","1815","1903","2019","2127","2162","2213","2296",
            "2343","2361","2391","2419","2567","2586","2666","2677","2783","3183","3566"
        };

        // The following configuration options are usually overwritten by server-side values:
        private string sessionName = "ajax_chat";
        private int cookieExpiration = 30;
        private string cookiePath = "/";
        private string cookieDomain = null;
        private bool cookieSecure = false;
        private string chatBotName = "Chatbot";
        private int chatBotID = 2147483647;
        private bool allowUserMessageDelete = true;
        private int inactiveTimeout = 600;
        private int privateChannelDiff = 500000000;
        private int privateMessageDiff = 1000000000;
        private bool showChannelMessages = true;
        private int messageTextMaxLength = 1040;
        private bool socketServerEnabled = false;
        private string socketServerHost = "localhost";
        private int socketServerPort = 1935;
        private int socketServerChatID = 0;

        /// <summary>
        /// The channel ID of the channel to enter on login. 
        /// The <see cref="LoginChannelName"/> is used if set to null.
        /// </summary>
        public int? LoginChannelID
        {
            get { return this.loginChannelID; }
            set
            {
                if (this.loginChannelID != value)
                {
                    this.loginChannelID = value;
                }
            }
        }

        /// <summary>
        /// The channelName of the channel to enter on login.
        /// The default channel is used if set to null.
        /// </summary>
        public string LoginChannelName
        {
            get { return this.loginChannelName; }
            set
            {
                if (this.loginChannelName != value)
                {
                    this.loginChannelName = value;
                }
            }
        }

        /// <summary>
        /// The time in milliseconds between update calls to retrieve new chat messages.
        /// </summary>
        public int TimerRate
        {
            get { return this.timerRate; }
            set
            {
                if (this.timerRate != value)
                {
                    this.timerRate = value;
                }
            }
        }

        /// <summary>
        /// The URL to retrieve the XML chat messages (must at least contain one parameter).
        /// </summary>
        public string AjaxURL
        {
            get { return this.ajaxURL; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (!this.ajaxURL.Equals(value, StringComparison.Ordinal))
                {
                    // TODO: Check if url is valid
                    this.ajaxURL = value;
                }
            }
        }

        /// <summary>
        /// The base URL of the chat directory, used to retrieve media files (images, sound files, etc).
        /// </summary>
        public string BaseURL
        {
            get { return this.baseURL; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (!this.baseURL.Equals(value, StringComparison.Ordinal))
                {
                    // TODO: Check if url is value
                    this.baseURL = value;
                }
            }
        }

        /// <summary>
        /// A regular expression pattern for allowed source URLs for media content (e.g. images displayed inline).
        /// </summary>
        public string RegExpMediaUrl
        {
            get { return this.regExpMediaURL; }
            set
            {
                if (this.regExpMediaURL != value)
                {
                    this.regExpMediaURL = value;
                }
            }
        }

        /// <summary>
        /// If set to false, the chat update is delayed until the event defined in ajaxChat.setStartChatHandler().
        /// </summary>
        public bool StartChatOnLoad
        {
            get { return this.startChatOnLoad; }
            set
            {
                if (this.startChatOnLoad != value)
                {
                    this.startChatOnLoad = value;
                }
            }
        }

        /// <summary>
        /// Defines the IDs of DOM nodes accessed by the chat.
        /// </summary>
        public DOM_IDs DomIDs
        {
            get { return this.domIDs; }
        }

        /// <summary>
        /// Defines the settings which can be modified by users.
        /// </summary>
        public AjaxChatSettings Settings
        {
            get { return this.settings; }
        }

        /// <summary>
        /// Defines a list of settings which are not to be stored in a session cookie.
        /// </summary>
        public string[] NonPersistentSettings
        {
            get { return this.nonPersistentSettings; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (value.Length == 0)
                {
                    this.nonPersistentSettings = new string[0];
                }
                else
                {
                    this.nonPersistentSettings = new string[value.Length];
                    Array.Copy(value, 0, this.nonPersistentSettings, 0, value.Length);
                }
            }
        }

        /// <summary>
        /// Defines the list of allowed BBCodes.
        /// </summary>
        public string[] BBCodeTags
        {
            get { return this.bbCodeTags; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (value.Length == 0)
                {
                    this.bbCodeTags = new string[0];
                }
                else
                {
                    this.bbCodeTags = new string[value.Length];
                    Array.Copy(value, 0, this.bbCodeTags, 0, value.Length);
                }
            }
        }

        /// <summary>
        /// Defines the list of allowed color codes.
        /// </summary>
        public string[] ColorCodes
        {
            get { return this.colorCodes; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (value.Length == 0)
                {
                    this.colorCodes = new string[0];
                }
                else
                {
                    this.colorCodes = new string[value.Length];
                    Array.Copy(value, 0, this.colorCodes, 0, value.Length);
                }
            }
        }

        /// <summary>
        /// Defines the list of allowed emoticon codes.
        /// </summary>
        public string[] EmoticonCodes
        {
            get { return this.emoticonCodes; }
        }

        /// <summary>
        /// Defines the list of emoticon files associated with the <see cref="EmoticonCodes"/>. 
        /// </summary>
        public string[] EmoticonFiles
        {
            get { return this.emoticonFiles; }
        }

        /// <summary>
        /// Defines the available sounds loaded on chat start.
        /// </summary>
        public Sound_Files SoundFiles
        {
            get { return this.soundFiles; }
            set
            {
                if (!this.soundFiles.Equals(value))
                {
                    this.soundFiles = value;
                }
            }
        }

        /// <summary>
        /// This was added by Inge.
        /// </summary>
        public string[] UserIcons
        {
            get { return this.userIcons; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (value.Length == 0)
                {
                    this.userIcons = new string[0];
                }
                else
                {
                    this.userIcons = new string[value.Length];
                    Array.Copy(value, 0, this.userIcons, 0, value.Length);
                }
            }
        }

        /// <summary>
        /// Session identification, used for style and setting cookies
        /// </summary>
        public string SessionName
        {
            get { return this.sessionName; }
            set
            {
                if (this.sessionName != value)
                {
                    this.sessionName = value;
                }
            }
        }

        /// <summary>
        /// The time in days until the style and setting cookies expire.
        /// </summary>
        public int CookieExpiration
        {
            get { return this.cookieExpiration; }
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException("value");
                if (this.cookieExpiration != value)
                {
                    this.cookieExpiration = value;
                }
            }
        }

        /// <summary>
        /// The path of the cookies. 
        /// "/" allows the chat to read the cookies from all directories.
        /// </summary>
        public string CookiePath
        {
            get { return this.cookiePath; }
            set
            {
                if (this.cookiePath != value)
                {
                    this.cookiePath = value;
                }
            }
        }

        /// <summary>
        /// The domain of the cookies.
        /// Defaults to the hostname of the server if set to null.
        /// </summary>
        public string CookieDomain
        {
            get { return this.cookieDomain; }
            set
            {
                if (this.cookieDomain != value)
                {
                    this.cookieDomain = value;
                }
            }
        }

        /// <summary>
        /// If enabled, cookies must be sent over secure (SSL/TLS encrypted) connections.
        /// </summary>
        public bool CookieSecure
        {
            get { return this.cookieSecure; }
            set
            {
                if (this.cookieSecure != value)
                {
                    this.cookieSecure = value;
                }
            }
        }

        /// <summary>
        /// The name of the chat bot.
        /// </summary>
        public string ChatBotName
        {
            get { return this.chatBotName; }
            set
            {
                if (string.IsNullOrEmpty(value))
                    throw new ArgumentNullException("value");
                if (this.chatBotName != value)
                {
                    this.chatBotName = value;
                }
            }
        }

        /// <summary>
        /// The userID of the chat bot.
        /// </summary>
        public int ChatBotID
        {
            get { return this.chatBotID; }
            set
            {
                if (this.chatBotID != value)
                {
                    this.chatBotID = value;
                }
            }
        }

        /// <summary>
        /// Allow/Disallow registered users to delete their own messages.
        /// </summary>
        public bool AllowUserMessageDelete
        {
            get { return this.allowUserMessageDelete; }
            set
            {
                if (this.allowUserMessageDelete != value)
                {
                    this.allowUserMessageDelete = value;
                }
            }
        }

        /// <summary>
        /// Minutes until a user is declared inactive (last status update).
        /// The minimum is 2 minutes.
        /// </summary>
        public int InactiveTimeout
        {
            get { return this.inactiveTimeout; }
            set
            {
                if (value < 2)
                    throw new ArgumentOutOfRangeException("value");
                if (this.inactiveTimeout != value)
                {
                    this.inactiveTimeout = value;
                }
            }
        }

        /// <summary>
        /// UserID plus this value are private channels.
        /// This is also the maximum userID and maximum channelID.
        /// </summary>
        public int PrivateChannelDiff
        {
            get { return this.privateChannelDiff; }
            set
            {
                if (this.privateChannelDiff != value)
                {
                    this.privateChannelDiff = value;
                }
            }
        }

        /// <summary>
        /// UserID plus this value are used for private messages.
        /// </summary>
        public int PrivateMessageDiff
        {
            get { return this.privateMessageDiff; }
            set
            {
                if (this.privateMessageDiff != value)
                {
                    this.privateMessageDiff = value;
                }
            }
        }

        /// <summary>
        /// Defines if login/logout and channel enter/leave are displayed.
        /// </summary>
        public bool ShowChannelMessages
        {
            get { return this.showChannelMessages; }
            set
            {
                if (this.showChannelMessages != value)
                {
                    this.showChannelMessages = value;
                }
            }
        }

        /// <summary>
        /// Maximum message text length.
        /// </summary>
        public int MessageTextMaxLength
        {
            get { return this.messageTextMaxLength; }
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException("value");
                if (this.messageTextMaxLength != value)
                {
                    this.messageTextMaxLength = value;
                }
            }
        }

        /// <summary>
        /// Defines if the socket server is enabled.
        /// </summary>
        public bool SocketServerEnabled
        {
            get { return this.socketServerEnabled; }
            set
            {
                if (this.socketServerEnabled != value)
                {
                    this.socketServerEnabled = value;
                }
            }
        }

        /// <summary>
        /// Defines the hostname of the socket server used to connect from client side.
        /// </summary>
        public string SocketServerHost
        {
            get { return this.socketServerHost; }
            set
            {
                if (string.IsNullOrEmpty(value))
                    throw new ArgumentNullException("value");
                if (!this.socketServerHost.Equals(value, StringComparison.Ordinal))
                {
                    // TODO: Check if host name is valid
                    this.socketServerHost = value;
                }
            }
        }

        /// <summary>
        /// Defines the port of the socket server
        /// </summary>
        public int SocketServerPort
        {
            get { return this.socketServerPort; }
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("value");
                if (this.socketServerPort != value)
                {
                    this.socketServerPort = value;
                }
            }
        }

        /// <summary>
        /// This ID can be used to distinguish between different chat installations using the same socket server.
        /// </summary>
        public int SocketServerChatID
        {
            get { return this.socketServerChatID; }
            set
            {
                if (this.socketServerChatID != value)
                {
                    this.socketServerChatID = value;
                }
            }
        }
    }
}
